const std = @import("std");
const builtin = @import("builtin");

fn buildWeb(b: *std.Build, target: std.Build.ResolvedTarget) void {
    const options = .{
        .optimize = b.standardOptimizeOption(.{
            .preferred_optimize_mode = .ReleaseSmall,
        }),
        .target = target,
    };

    const activate_emsdk = @import("zemscripten").activateEmsdkStep(b);
    b.default_step.dependOn(activate_emsdk);

    // inline for (comptime std.meta.declarations(samples.web)) |d| {
    const build_web_app_step = @import("src/minimal_glfw_gl/build.zig").buildWeb(b, options);
    build_web_app_step.dependOn(activate_emsdk);

    b.getInstallStep().dependOn(build_web_app_step);

    const html_filename = std.fmt.allocPrint(
        b.allocator,
        "{s}.html",
        .{"minimal_glfw_gl"},
    ) catch unreachable;

    const emrun_step = @import("zemscripten").emrunStep(
        b,
        b.getInstallPath(.{ .custom = "web" }, html_filename),
        &.{},
    );
    emrun_step.dependOn(build_web_app_step);

    b.step(
        "minimal_glfw_gl",
        "Build '" ++ "minimal_glfw_gl" ++ "' sample as a web app",
    ).dependOn(build_web_app_step);

    b.step(
        "minimal_glfw_gl" ++ "-run",
        "Build '" ++ "minimal_glfw_gl" ++ "' sample as a web app and serve locally using `emrun`",
    ).dependOn(emrun_step);
}

fn buildDesktop(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) void {
    // const optimize = b.standardOptimizeOption(.{});

    const zpix_enable = b.option(
        bool,
        "zpix-enable",
        "Enable PIX for Windows profiler",
    ) orelse false;
    const options = .{
        .optimize = optimize,
        .target = target,
        .zxaudio2_debug_layer = b.option(
            bool,
            "zxaudio2_debug_layer",
            "Enable XAudio2 debug layer",
        ) orelse false,
        .zd3d12_debug_layer = b.option(
            bool,
            "zd3d12_debug_layer",
            "Enable DirectX 12 debug layer",
        ) orelse false,
        .zd3d12_gbv = b.option(
            bool,
            "zd3d12_gbv",
            "Enable DirectX 12 GPU-Based Validation (GBV)",
        ) orelse false,
        .zpix_enable = zpix_enable,
        .zpix_path = b.option(
            []const u8,
            "zpix-path",
            "Installed PIX path",
        ) orelse if (zpix_enable) @panic("PIX path is required when enabled") else "",
    };

    // if (target.result.os.tag == .windows) {
    //     if (builtin.target.os.tag == .windows or builtin.target.os.tag == .linux) {
    //         const activate_zwindows = @import("zwindows").activateSdk(b, b.dependency("zwindows", .{}));
    //         b.default_step.dependOn(activate_zwindows);

    //         // inline for (comptime std.meta.declarations(samples.windows_linux_cross)) |d| {
    //         //     _ = buildExe(b, options, @field(samples.windows_linux_cross, d.name));
    //         // }

    //         if (builtin.target.os.tag == .windows) {
    //             // TODO: Try to upgrade these to windows_linux_cross
    //             inline for (comptime std.meta.declarations(samples.windows_only)) |d| {
    //                 _ = buildExe(b, options, @field(samples.windows_only, d.name));
    //             }
    //         }
    //     } else @panic("Unsupported host OS for Windows target");
    // }

    // inline for (comptime std.meta.declarations(samples.crossplatform)) |d| {
    //     _ = buildExe(b, options, @field(samples.crossplatform, d.name));
    // }
    // _ = buildExe(b, options, @import("src/minimal_glfw_gl/build.zig"));
    _ = buildExe(b, options, @import("src/minimal_glfw_gl/build.zig"));

    // Install prebuilt SDL2 libs in bin output dir
    // if (@import("zsdl").prebuilt_sdl2.install(b, options.target.result, .bin, .{
    //     .ttf = true,
    //     .image = true,
    // })) |install_sdl2_step| {
    //     b.getInstallStep().dependOn(install_sdl2_step);
    // }

    // buildBenchmarks(b, options.target);

    // Experiments
    // if (b.option(bool, "experiments", "Build our prototypes and experimental programs") orelse false) {
    //     @import("experiments/build.zig").build(b, options);
    // }
}

fn buildExe(b: *std.Build, options: anytype, sample: anytype) *std.Build.Step.Compile {
    const exe = sample.build(b, options);

    if (exe.rootModuleTarget().os.tag == .windows) {
        // TODO: Problems with LTO on Windows.
        exe.want_lto = false;
    }

    if (exe.root_module.optimize != .Debug) {
        exe.root_module.strip = true;
    }

    const install_exe = b.addInstallArtifact(exe, .{});
    b.getInstallStep().dependOn(&install_exe.step);
    b.step(sample.demo_name, "Build '" ++ sample.demo_name ++ "' demo").dependOn(&install_exe.step);

    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(&install_exe.step);
    b.step(sample.demo_name ++ "-run", "Run '" ++ sample.demo_name ++ "' demo").dependOn(&run_cmd.step);

    return exe;
}
// Although this function looks imperative, it does not perform the build
// directly and instead it mutates the build graph (`b`) that will be then
// executed by an external runner. The functions in `std.Build` implement a DSL
// for defining build steps and express dependencies between them, allowing the
// build runner to parallelize the build automatically (and the cache system to
// know when a step doesn't need to be re-run).
pub fn build(b: *std.Build) void {
    // Standard target options allow the person running `zig build` to choose
    // what target to build for. Here we do not override the defaults, which
    // means any target is allowed, and the default is native. Other options
    // for restricting supported target set are available.
    const target = b.standardTargetOptions(.{});
    // Standard optimization options allow the person running `zig build` to select
    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
    // set a preferred release mode, allowing the user to decide how to optimize.
    const optimize = b.standardOptimizeOption(.{});
    switch (target.result.os.tag) {
        .emscripten => buildWeb(b, target),
        else => buildDesktop(b, target, optimize),
    }

    // const mod = b.addModule("zviewer", .{
    //     // The root source file is the "entry point" of this module. Users of
    //     // this module will only be able to access public declarations contained
    //     // in this file, which means that if you have declarations that you
    //     // intend to expose to consumers that were defined in other files part
    //     // of this module, you will have to make sure to re-export them from
    //     // the root file.
    //     .root_source_file = b.path("src/root.zig"),
    //     // Later on we'll use this module as the root module of a test executable
    //     // which requires us to specify a target.
    //     .target = target,
    // });

    // const exe = b.addExecutable(.{
    //     .name = "zviewer",
    //     .root_module = b.createModule(.{
    //         // b.createModule defines a new module just like b.addModule but,
    //         // unlike b.addModule, it does not expose the module to consumers of
    //         // this package, which is why in this case we don't have to give it a name.
    //         .root_source_file = b.path("src/main.zig"),
    //         // Target and optimization levels must be explicitly wired in when
    //         // defining an executable or library (in the root module), and you
    //         // can also hardcode a specific target for an executable or library
    //         // definition if desireable (e.g. firmware for embedded devices).
    //         .target = target,
    //         .optimize = optimize,
    //         // List of modules available for import in source files part of the
    //         // root module.
    //         .imports = &.{
    //             // Here "zviewer" is the name you will use in your source code to
    //             // import this module (e.g. `@import("zviewer")`). The name is
    //             // repeated because you are allowed to rename your imports, which
    //             // can be extremely useful in case of collisions (which can happen
    //             // importing modules from different packages).
    //             .{ .name = "zviewer", .module = mod },
    //         },
    //     }),
    // });

    // // This declares intent for the executable to be installed into the
    // // install prefix when running `zig build` (i.e. when executing the default
    // // step). By default the install prefix is `zig-out/` but can be overridden
    // // by passing `--prefix` or `-p`.
    // b.installArtifact(exe);

    // const run_step = b.step("run", "Run the app");

    // const run_cmd = b.addRunArtifact(exe);
    // run_step.dependOn(&run_cmd.step);

    // // By making the run step depend on the default step, it will be run from the
    // // installation directory rather than directly from within the cache directory.
    // run_cmd.step.dependOn(b.getInstallStep());

    // // This allows the user to pass arguments to the application in the build
    // // command itself, like this: `zig build run -- arg1 arg2 etc`
    // if (b.args) |args| {
    //     run_cmd.addArgs(args);
    // }

    // // Creates an executable that will run `test` blocks from the provided module.
    // // Here `mod` needs to define a target, which is why earlier we made sure to
    // // set the releative field.
    // const mod_tests = b.addTest(.{
    //     .root_module = mod,
    // });

    // // A run step that will run the test executable.
    // const run_mod_tests = b.addRunArtifact(mod_tests);

    // // Creates an executable that will run `test` blocks from the executable's
    // // root module. Note that test executables only test one module at a time,
    // // hence why we have to create two separate ones.
    // const exe_tests = b.addTest(.{
    //     .root_module = exe.root_module,
    // });

    // // A run step that will run the second test executable.
    // const run_exe_tests = b.addRunArtifact(exe_tests);

    // // A top level step for running all tests. dependOn can be called multiple
    // // times and since the two run steps do not depend on one another, this will
    // // make the two of them run in parallel.
    // const test_step = b.step("test", "Run tests");
    // test_step.dependOn(&run_mod_tests.step);
    // test_step.dependOn(&run_exe_tests.step);

}
